package service

import (
	"fly/global"
	"fly/helper"
	"fly/models"
	"fly/resp"
	"fly/schema"
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	"github.com/duke-git/lancet/v2/condition"
	"github.com/duke-git/lancet/v2/convertor"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/duke-git/lancet/v2/strutil"
	"net/url"
	"time"
)

const (
	AircraftCacheSimple = "AircraftCacheSimpleV1:%d"
)

type AircraftService struct {
	BaseService
	SysDictService SysDictService
	AirportService AirportService
}

func (c *AircraftService) GetPaginateData(params url.Values, deptIds []int64) ([]*models.Aircraft, Pagination) {
	c.WhereField = append(c.WhereField, new(models.Aircraft).WhereField()...)
	var data []*models.Aircraft
	o := orm.NewOrm().QueryTable(new(models.Aircraft))
	_, err := c.PaginateAndScopeWhere(o, params, func(cond *orm.Condition) *orm.Condition {
		startTime := params.Get("startTime")
		endTime := params.Get("endTime")
		if len(startTime) > 0 && len(endTime) > 0 {
			start, err1 := convertor.ToInt(startTime)
			end, err2 := convertor.ToInt(endTime)
			if err1 == nil && err2 == nil {
				cond = cond.And("create_time__gte", helper.Milli2ChinaStr(start)).And("create_time__lte",
					helper.Milli2ChinaStr(end))
			}
		}
		if experienceId := params.Get("experienceId"); len(experienceId) > 0 {
			// 先查有指定经历的飞机
			var experiences []*models.AircraftExperience
			_, _ = orm.NewOrm().QueryTable(new(models.AircraftExperience)).Filter("experience_dict_id", experienceId).All(&experiences, "aircraft_id")
			if len(experiences) > 0 {
				ids := slice.Map(experiences, func(index int, item *models.AircraftExperience) int64 {
					return item.AircraftId
				})
				cond = cond.And("id__in", ids)
			}
		}
		if params.Has("deptId") {
			cond = cond.And("dept_id", params.Get("deptId"))
		} else {
			cond = cond.And("dept_id__in", deptIds)
		}
		cond = condition.TernaryOperator(params.Has("typeDictId"), cond.And("type_dict_id", params.Get("typeDictId")), cond)
		cond = condition.TernaryOperator(params.Has("stateDictId"), cond.And("state_dict_id", params.Get("stateDictId")), cond)
		return cond
	}).All(&data)
	if err != nil {
		return nil, c.Pagination
	}
	return data, c.Pagination
}

// GetIdsByDeptId 根据部门下的飞机ids
func (c *AircraftService) GetIdsByDeptId(deptId int64) []int64 {
	var list []*models.Aircraft
	qs := orm.NewOrm().QueryTable(new(models.Aircraft)).Filter("dept_id", deptId)
	_, err := qs.All(&list, "id")
	if err != nil {
		return nil
	}
	ids := slice.Map(list, func(index int, item *models.Aircraft) int64 {
		return item.Id
	})
	return ids
}

// GetAllAircraft 查询所有可用飞机
func (c *AircraftService) GetAllAircraft(deptIds []int64) (list []*models.Aircraft, err error) {
	_, err = orm.NewOrm().QueryTable(new(models.Aircraft)).Filter("dept_id__in", deptIds).All(&list)
	return
}

// GetAircraftByLocation 查询指定机位上的飞机
func (c *AircraftService) GetAircraftByLocation(locationId int64) (list []*models.Aircraft, err error) {
	_, err = orm.NewOrm().QueryTable(new(models.Aircraft)).Filter("location_id", locationId).All(&list)
	return
}

// EnableLocationPutAircraft 指定机位上是否还能保存飞机
func (c *AircraftService) EnableLocationPutAircraft(locationId int64) (bool, error) {
	list, err := c.GetAircraftByLocation(locationId)
	if err != nil {
		return false, err
	}
	location, err := models.GetAircraftLocationById(locationId)
	if err != nil {
		return false, err
	}
	return len(list) == 0 || location.Type == schema.LocationTypeRepair, nil
}

func (c *AircraftService) GetSimpleAircraftById(id int64) (*resp.SimpleAircraft, error) {
	key := fmt.Sprintf(AircraftCacheSimple, id)
	return global.GetCache[*resp.SimpleAircraft](key, func() (*resp.SimpleAircraft, error) {
		aircraft := models.Aircraft{}
		mAircraft := new(models.Aircraft)
		newOrm := orm.NewOrm()
		err := newOrm.QueryTable(mAircraft).Filter("id", id).One(&aircraft, mAircraft.SimpleField()...)
		simpleAircraft := resp.SimpleAircraft{}
		err = helper.CopyDeep(&simpleAircraft, aircraft)
		if err != nil {
			return nil, err
		}
		simpleAircraft.DeptName, _ = c.SysDictService.GetDictValueById(aircraft.DeptId)
		simpleAircraft.StateName, _ = c.SysDictService.GetDictValueById(aircraft.StateDictId)
		simpleAircraft.TypeName, _ = c.SysDictService.GetDictValueById(aircraft.TypeDictId)
		return &simpleAircraft, err
	})
}

func (c *AircraftService) GetSimpleAircraftByCode(code string) (*resp.SimpleAircraft, error) {
	aircraft := models.Aircraft{}
	mAircraft := new(models.Aircraft)
	newOrm := orm.NewOrm()
	err := newOrm.QueryTable(mAircraft).Filter("code", code).One(&aircraft, mAircraft.SimpleField()...)
	if err != nil {
		return nil, err
	}
	simpleAircraft := resp.SimpleAircraft{}
	err = helper.CopyDeep(&simpleAircraft, aircraft)
	simpleAircraft.DeptName, _ = c.SysDictService.GetDictValueById(aircraft.DeptId)
	simpleAircraft.StateName, _ = c.SysDictService.GetDictValueById(aircraft.StateDictId)
	simpleAircraft.TypeName, _ = c.SysDictService.GetDictValueById(aircraft.TypeDictId)
	return &simpleAircraft, err
}

func (c *AircraftService) UpdateAircraftStatusByTaskStatus(taskStatus int, aircraftId, userId int64) (err error) {
	aircraft, err := models.GetAircraftById(aircraftId)
	if err != nil || aircraft == nil {
		return err
	}
	switch taskStatus {
	case schema.FlightTaskBreakCancel:
		faultDict, err := c.SysDictService.GetDictByCodeKey(schema.AircraftStateKey, schema.AircraftFaultKey)
		if err != nil || faultDict == nil {
			return err
		}
		// 故障取消，需要更新飞机的状态
		aircraft.StateDictId = faultDict.Id
		aircraft.Status = schema.Disable
		break
	case schema.FlightTaskFinish:
		// 完成，认为在3小时内，更新为已完成
		aircraft.Status = schema.AircraftTaskFinish
		break
	case schema.FlightTaskCancel:
		// 任务取消，飞机为空闲
		aircraft.Status = schema.AircraftIdle
		break
	case schema.FlightTaskExecuting:
		// 任务执行中，飞机飞行中
		aircraft.Status = schema.AircraftInFlight
		break
	case schema.FlightTaskInvalid:
		// 任务失效，飞机为空闲
		aircraft.Status = schema.AircraftIdle
		break
	}
	err = models.UpdateAircraftById(aircraft, userId)
	return err
}

func (c *AircraftService) UpdateAllAircraftStatus() (err error) {
	var list []*models.Aircraft
	o := orm.NewOrm()
	_, err = o.QueryTable(new(models.Aircraft)).Filter("status__gte", schema.Enabled).Filter("enabled__gte", schema.Enabled).All(&list)
	if err != nil || len(list) == 0 {
		return err
	}
	for _, aircraft := range list {
		newStatus, err := c.calStatus(aircraft, o)
		if err == nil && newStatus != aircraft.Status {
			_, _ = o.Update(aircraft, "status")
			_ = global.DelCache(fmt.Sprintf(AircraftCacheSimple, aircraft.Id))
		}
	}
	return nil
}

func (c *AircraftService) calStatus(aircraft *models.Aircraft, o orm.Ormer) (status int, err error) {
	var tasks []*models.FlightTask
	_, err = o.QueryTable(new(models.FlightTask)).Filter("aircraft_id", aircraft.Id).Filter("status__gte", schema.Enabled).All(&tasks)
	if err != nil {
		return 0, err
	}
	// 若有当前时间在计划开始时间30min之内，则有任务未执行
	_, ok := slice.FindBy(tasks, func(index int, item *models.FlightTask) bool {
		return item.PlanStart != nil && time.Now().Before(item.PlanStart.Add(30*time.Minute))
	})
	if ok {
		return schema.AircraftTaskWait, nil
	}
	// 若有正计划时间范围内的，认为正在飞行中
	_, ok = slice.FindBy(tasks, func(index int, item *models.FlightTask) bool {
		return item.PlanStart != nil && item.PlanEnd != nil && item.PlanStart.Before(time.Now()) && item.PlanEnd.After(time.Now())
	})
	if ok {
		return schema.AircraftInFlight, nil
	}
	// 若有正计划时间完成3小时间内，认为任务已完成
	_, ok = slice.FindBy(tasks, func(index int, item *models.FlightTask) bool {
		return item.PlanEnd != nil && item.PlanEnd.After(time.Now().Add(3*time.Hour))
	})
	if ok {
		return schema.AircraftTaskFinish, nil
	}
	return schema.AircraftIdle, nil
}

// IsFlightPool 查询指定飞机是否在飞行池中
// 任务中&&目标机场不是邛崃; 飞机状态为返厂
func (c *AircraftService) IsFlightPool(aircraft *models.Aircraft) (isPool bool, err error) {
	if aircraft == nil {
		return
	}
	dict, err := c.SysDictService.GetSimpleDictById(aircraft.StateDictId)
	if dict.DicCode == schema.AircraftStateKey && dict.DicKey == schema.AircraftFactoryKey {
		// 飞机状态为返厂
		isPool = true
		return
	}
	if aircraft.Status == schema.AircraftInFlight {
		// 任务中，判断目标机场是否为邛崃
		var tasks []*models.FlightTask
		_, err = orm.NewOrm().QueryTable(new(models.FlightTask)).Filter("aircraft_id", aircraft.Id).Filter("status", schema.FlightTaskExecuting).All(&tasks)
		if err != nil {
			return
		}
		_, ok := slice.FindBy(tasks, func(index int, item *models.FlightTask) bool {
			airport, _ := c.AirportService.GetSimpleAirportById(item.TargetAirportId)
			// airport.Name 包含邛崃
			return airport != nil && strutil.ContainsAny(airport.Name, []string{"邛崃"})
		})
		isPool = ok
	}
	return
}
